#include <OSGConfig.h>

#include <iostream>

#include <OSGFieldContainerFactory.h>
#include <OSGSFSysTypes.h>
#include <OSGNode.h>
#include <OSGThread.h>
#include <OSGAttachment.h>
#include <OSGMFVecTypes.h>
#include <OSGBaseFunctions.h>
#include <OSGChangeList.h>
#include <OSGFieldContainer.h>
#include <OSGGroup.h>
#include <OSGFieldContainerPtr.h>
#include <OSGSimpleAttachments.h>
#include <OSGWindow.h>
#include <OSGViewport.h>
#include <OSGPassiveWindow.h>

#include <OSGFieldFactory.h>
#include <OSGSFVecTypes.h>
#include <OSGSFSysTypes.h>
#include <OSGBaseFunctions.h>
#include <OSGSFBaseTypes.h>
#include <OSGMFBaseTypes.h>
#include <OSGSFMathTypes.h>
#include <OSGMFMathTypes.h>
#include <OSGMFSysTypes.h>

#include <assert.h>

// Things to test

void descFCType(osg::FieldContainerType* fcType);
void descFDesc(osg::FieldDescription* fieldDesc);
void descField(osg::Field* field);
void descFieldType(const osg::FieldType* fieldType);
/*
template <class T> void setField(osg::FieldContainerPtr fc, char * fieldName, T newValue);
template <class T> T getField(osg::FieldContainerPtr fc, char * fieldName, T* dummy = NULL);

template <class T> void setFieldValue(osg::Field* field, T newValue);
template <class T> T getFieldValue(osg::Field* field, T* dummy = NULL);
*/


void descFCType(osg::FieldContainerType* fcType)
{
   std::string fc_type_name(fcType->getCName());
   osg::UInt32 fc_type_id(fcType->getId());

   std::cerr << "FC Type: [" << fc_type_id << "] " << fc_type_name << std::endl;
   std::cerr << "  Init:" << fcType->isInitialized()
             << "  Abstract:" << fcType->isAbstract()
             << "  Node:" << fcType->isNode()
             << "  NodeCore:" << fcType->isNodeCore()
             << "  Attach:" << fcType->isAttachment()
             << std::endl << std::flush;

   osg::UInt32 num_fields = fcType->getNumFieldDescs();
   std::cerr << "  Fields:" << num_fields << std::endl;
   for(osg::UInt32 i=0; i<num_fields; ++i)
   {  
      std::cerr << "    " << i << ": ";
      descFDesc(fcType->getFieldDescription(i));
      std::cerr << std::endl;
   }
   std::cerr << std::endl;

}

void descFDesc(osg::FieldDescription* fieldDesc)
{
   if(NULL == fieldDesc)
   {
      std::cerr << "N/A";
   }
   else
   {
      std::string fd_name(fieldDesc->getCName());
      osg::UInt32 field_id(fieldDesc->getFieldId());
      std::string type_name(fieldDesc->getFieldType().getCName());
   
      std::cerr << fd_name << "  id:" << field_id << "  type:" << type_name;
   }
}

void descField(osg::Field* field)
{
   descFieldType(&(field->getType()));
   std::cerr << "  content: " << field->getContentType().getCName() << std::endl;

}

void descFieldType(const osg::FieldType* fieldType)
{
   std::string field_type_name(fieldType->getCName());
   bool multi(osg::FieldType::MULTI_FIELD == fieldType->getCardinality());

   std::cerr << "name: " << field_type_name << "  multi:" << multi;
}


// Functions for generically accessing fields
template <class T>
void setField(osg::FieldContainerPtr fc, char * fieldName, T newValue)
{
    const osg::FieldDescription * desc    = fc->getType().findFieldDescription(fieldName); 
    osg::Field *                  field   = fc->getField(desc->getFieldId()); 
    osg::SField<T> *              dataptr = dynamic_cast<osg::SField<T>*>(field); 

    if (dataptr)
        dataptr->setValue(newValue);
}

template <class T>
T getField(osg::FieldContainerPtr fc, char * fieldName, T * )
{
    const osg::FieldDescription * desc    = fc->getType().findFieldDescription(fieldName); 
    osg::Field *                  field   = fc->getField(desc->getFieldId()); 
    osg::SField<T> *              dataptr = dynamic_cast<osg::SField<T>*>(field); 

    if (dataptr)
        return dataptr->getValue();
    else
        return 0;
}

template <class T> 
void setFieldValue(osg::Field* field, T newValue)
{
   osg::SField<T>*   dataptr = dynamic_cast<osg::SField<T>*>(field);
   if(dataptr)
      dataptr->setValue(newValue);
}

template <class T> 
T getFieldValue(osg::Field* field, T* dummy = NULL)
{
   T ret_val;

   osg::SField<T>*   dataptr = dynamic_cast<osg::SField<T>*>(field);

   if(dataptr)
      ret_val = dataptr->getValue();

   return ret_val;
}

int main (int argc, char **argv)
{
   osg::ChangeList::setReadWriteDefault();
   osg::osgInit(argc, argv);

   osg::FieldContainerFactory* fcf = osg::FieldContainerFactory::the();
   osg::FieldFactory& ff(osg::FieldFactory::the());

   // -- FIELDS -- //
   // Creating fields
   osg::Field* field_ptr;
   osg::SFVec3f vec3f, *vec3f_ptr;
   osg::SFQuaternion sfquad;
   osg::MFQuaternion mfquad;
   osg::SFInt32* int32_ptr;
   osg::SFUInt32* uint32_ptr;

   field_ptr = ff.createField("SFVec3f");
   vec3f_ptr = dynamic_cast<osg::SFVec3f*>(field_ptr);
   assert(vec3f_ptr != NULL);

   // Check vector interface
   vec3f_ptr->getValue().setValues(1,2,3);
   osg::Vec3f pos = vec3f_ptr->getValue();
   pos[2] = 10;
   vec3f_ptr->getValue() = pos;

   int32_ptr = dynamic_cast<osg::SFInt32*>(ff.createField("SFInt32"));
   assert(NULL != int32_ptr);

   osg::Int32 int32_val = int32_ptr->getValue();
   int32_ptr->getValue() = (int32_val+10);
   int32_ptr->setValue(20);

   // Assign with abstrvalue interface
   osg::SFVec3f* vec3f_ptr2 = dynamic_cast<osg::SFVec3f*>(ff.createField("SFVec3f"));
   field_ptr->setAbstrValue(*vec3f_ptr2);

   // Multifield
   // Quaternion
   // Matrix
   

   // - Creating fc from factory
   //    - Create Field container
   //    - Create Node
   //    - Create NodeCore
   osg::FieldContainerPtr node_fc;
   osg::NodePtr           node_ptr;
   osg::NodeCorePtr       node_core_ptr;

   assert(node_fc == osg::NullFC);
   assert(node_ptr == osg::NullFC);
   assert(node_core_ptr == osg::NullFC);

   node_fc = fcf->createFieldContainer("Node");
   node_ptr = fcf->createNode("Node");
   node_ptr = osg::Node::create();
   node_core_ptr = fcf->createNodeCore("Group");

   // Groups???
   //    - List all groups
   // List all groups
/*
   std::cerr << "Groups:" << std::endl;
   osg::UInt16 num_groups = fcf->getNumGroups();
   for(osg::UInt16 gid=0; gid<num_groups;++gid)
   {
      std::cerr << gid << ": " << fcf->findGroupName(gid) << std::endl;
   }
   std::cerr << std::endl;
*/

   std::cerr << "Field Containers:" << std::endl << std::flush;
   osg::UInt16 num_fc_types = fcf->getNumTypes();
   for(osg::UInt16 i=0;i<num_fc_types;++i)
   {  
      osg::FieldContainerType* fc_type = fcf->findType(i);
      if(NULL != fc_type)
      { std::cerr << "  " << i << ": " << fc_type->getCName() << std::endl; }
   }
   std::cerr << std::endl << std::flush;

   // FieldContainerType
   //    - Output full description of type
   //       - Details of each FieldDescription
   osg::FieldContainerType* win_fc_type = fcf->findType("Window");
   descFCType(win_fc_type);

   // Field Container
   //  - Gettype
   //  - get type id
   //  - get groupid
   //  - GetClassType()
   std::cerr << "Viewport FieldContainer:\n";
   osg::FieldContainerPtr vp_fc;
   vp_fc = fcf->createFieldContainer("Viewport");
   std::cerr << "   type id:" << vp_fc->getTypeId() << std::endl
             << "   type name:" << vp_fc->getTypeName() << std::endl;

   osg::FieldContainerType* vp_fc_type = &(vp_fc->getType());
   std::cerr << "Viewport type:\n";
   descFCType(vp_fc_type);

   // Fields
   // - Reflective:
   //    - Get field type
   //    - get content type (and display)
   //    - Get cardinality

   osg::FieldContainerPtr win_fc = fcf->createFieldContainer("PassiveWindow");
   
   assert(osg::NullFC != win_fc);

   osg::Field* width_field = win_fc->getField("width");
   assert(NULL != width_field);
 
   std::cerr << "Window width field:\n";
   descField(width_field); 

   // Get the value for the field
   osg::UInt16 width_val = getFieldValue<osg::UInt16>(width_field);
   width_val += 20;
   setFieldValue<osg::UInt16>(width_field, width_val);

   // - Set and get values
   //    - setAbstrValue
   //    - Set with all primitive types
   osg::PassiveWindowPtr win_ptr = osg::PassiveWindow::create();
   osg::UInt16 width = win_ptr->getWidth();
   osg::SFUInt16* sf_width = win_ptr->getSFWidth();
   width = sf_width->getValue();

   win_ptr->setWidth(width+20);
   win_ptr->getSFWidth()->setValue(width+20);

//
// - Nodes
//    - Add/remove children
//    - Add/remove nodecores

   
   std::cerr << "\n\nDone..." << std::endl;
   osg::osgExit();
}


